package cn.qingfeng.nio.blocking.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * nio阻塞模式，服务端代码与非阻塞的一模一样
 */
public class NioServer {

    public static void main(String[] args) throws IOException {
        // 打开一个选择器
        Selector selector = Selector.open();

        // 打开一个ServerSocket通道
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(8080));
        //这里不能设置为true，否则报错IllegalBlockingModeException
        serverSocketChannel.configureBlocking(false);

        // 将通道注册到选择器上，并指定感兴趣的操作（OP_ACCEPT）
        // - SelectionKey.OP_ACCEPT主要用于服务器端，表示服务器准备接受来自客户端的连接请求。
        // - - 不注册SelectionKey.OP_ACCEPT的话客户端将无法建立连接，消息发布出去、也收不到回复。
        // - SelectionKey.OP_CONNECT主要用于客户端，表示客户端与服务器的连接已经建立成功或正在尝试建立连接。
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        while (true) {
            // 选择已经就绪的通道
            selector.select();

            // 获取所有已就绪的键的集合
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();

            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                iterator.remove();

                if (key.isAcceptable()) {
                    // 处理新的连接请求
                    // - 客户端调用SocketChannel.open之后走这里
                    ServerSocketChannel server = (ServerSocketChannel) key.channel();
                    SocketChannel client = server.accept();
                    client.configureBlocking(false);
                    client.register(selector, SelectionKey.OP_READ);
                    System.out.println("Accepted new connection from client: " + client.getRemoteAddress());
                } else if (key.isReadable()) {
                    // 读取数据
                    // - 客户端调用socketChannel.write之后走这里
                    SocketChannel client = (SocketChannel) key.channel();
                    ByteBuffer buffer = ByteBuffer.allocate(256);
                    int bytesRead = client.read(buffer);

                    if (bytesRead == -1) {
                        client.close();
                    } else {
                        buffer.flip();
                        byte[] bytes = new byte[buffer.remaining()];
                        buffer.get(bytes);
                        String message = new String(bytes);
                        System.out.println("Received message from client: " + message);

                        // 回显消息给客户端
                        buffer.rewind();
                        client.write(buffer);
                    }
                }
            }
        }
    }
}